package vsy.buzzwordbingo.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.NotSupportedException;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.thoughtworks.xstream.XStream;

import vsy.buzzwordbingo.model.DuplicatePlayerException;
import vsy.buzzwordbingo.model.Game;
import vsy.buzzwordbingo.model.GameBoard;
import vsy.buzzwordbingo.model.GameCore;
import vsy.buzzwordbingo.model.Player;
import vsy.buzzwordbingo.model.impl.PlayerImpl;
import vsy.buzzwordbingo.sort.ComparatorProvider;
import vsy.buzzwordbingo.util.ControllerUtil;

@Controller
public class RestController {
	
	@Resource
	private GameCore gameCore;
	
	@Resource
	private ComparatorProvider comparatorProvider;
	
	@RequestMapping(value = "/rest", method = RequestMethod.GET)
	public ModelAndView home(HttpServletRequest request,
			HttpServletResponse response) {
		prepare(request);
		return new ModelAndView("restResources");
	}

	@RequestMapping(value = "/rest/games", method = RequestMethod.GET)
	public ModelAndView getGames(HttpServletRequest request,
			HttpServletResponse response,
			@RequestParam(required = false) String from,
			@RequestParam(required = false) String to,
			@RequestParam(required = false) String sort,
			@RequestParam(required = false) String order) {
		
		List<Game> allGames = gameCore.getGames();
		
		Comparator<Game> comparator = comparatorProvider.getComparator(Game.class, sort, order);
		Collections.sort(allGames, comparator);
		
		List<Game> games = new PaginatedList<Game>(allGames, from, to, 100);
		
		request.setAttribute("games", games);
		prepare(request);
		return new ModelAndView("restGames");
	}
	
	@RequestMapping(value = "/rest/game/{id}", method = RequestMethod.GET)
	public ModelAndView getGame(HttpServletRequest request,
			HttpServletResponse response, @PathVariable String id) {
		
		Game game = gameCore.getGame(id);
				
		request.setAttribute("game", game);
		prepare(request);
		return new ModelAndView("restGame");
	}
	
	@RequestMapping(value = "/rest/players", method = RequestMethod.GET)
	public ModelAndView getPlayers(HttpServletRequest request,
			HttpServletResponse response,
			@RequestParam(required = false) String from,
			@RequestParam(required = false) String to,
			@RequestParam(required = false) String sort,
			@RequestParam(required = false) String order) {
		
		List<Player> allPlayers = gameCore.getPlayers();
		
		Comparator<Player> comparator = comparatorProvider.getComparator(Player.class, sort, order);
		Collections.sort(allPlayers, comparator);
		
		List<Player> players = new PaginatedList<Player>(allPlayers, from, to, 1);
		
		request.setAttribute("players", players);
		prepare(request);
		return new ModelAndView("restPlayers");
	}
	
	@RequestMapping(value = "/rest/players/{name}", method = RequestMethod.GET)
	public ModelAndView getPlayer(HttpServletRequest request,
			HttpServletResponse response, @PathVariable String name) {
		
		Player player = gameCore.getPlayer(name);
				
		request.setAttribute("player", player);
		prepare(request);
		return new ModelAndView("restPlayer");
	}
	
	@RequestMapping(value = "/rest/boards", method = RequestMethod.GET)
	public ModelAndView getBoards(HttpServletRequest request,
			HttpServletResponse response,
			@RequestParam(required = false) String from,
			@RequestParam(required = false) String to,
			@RequestParam(required = false) String game,
			@RequestParam(required = false) String player) throws NotSupportedException {
		
		List<GameBoard> boards;
		if (game != null && player != null) {
			boards = new ArrayList<GameBoard>(1);
			boards.add(gameCore.getBoard(game, player));
		}
		else if (player != null) {
			boards = gameCore.getPlayer(player).getBoards();
		}
		else if (game != null) {
			boards = gameCore.getGame(game).getBoards();
		}
		else {
			//TODO
			throw new NotSupportedException();
		}
		
		request.setAttribute("boards", boards);
		prepare(request);
		return new ModelAndView("restBoards");
	}
	
	@RequestMapping(value = "/rest/boards/{id}", method = RequestMethod.GET)
	public ModelAndView getBoard(HttpServletRequest request,
			HttpServletResponse response, @PathVariable String id) {
		
//		request.setAttribute("board", board);
		prepare(request);
		return new ModelAndView("restBoard");
	}
	
	//LOGIN
	@RequestMapping(value = "/rest/players", method = RequestMethod.POST)
	public ModelAndView loginPlayer(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		XStream xstream = new XStream();
		xstream.alias("player", PlayerBuilder.class);
		Player player = ((PlayerBuilder) xstream.fromXML(request.getInputStream())).createPlayer();
		
		try {
			gameCore.loginPlayer(player);
		} catch (DuplicatePlayerException e) {
			//TODO
		}
		
		//TODO Location
		return ControllerUtil.createEmptyResponse("text/plain", HttpStatus.CREATED);
	}
	
	//LOGOUT
	@RequestMapping(value = "/rest/players/{name}", method = RequestMethod.DELETE)
	public ModelAndView logoutPlayer(HttpServletRequest request,
			HttpServletResponse response, @PathVariable String name) throws IOException {
		String requestPlayer = request.getUserPrincipal().getName();
		
		if (!requestPlayer.equals(name)) {
			return ControllerUtil.createEmptyResponse("text/plain", HttpStatus.FORBIDDEN);
		}
		
		gameCore.logoutPlayer(name);		
		
		return ControllerUtil.createEmptyResponse("text/plain", HttpStatus.OK);
	}	
	
	//LOGOUT
	@RequestMapping(value = "/rest/games/", method = RequestMethod.POST)
	public ModelAndView createGame(HttpServletRequest request,
			HttpServletResponse response, @PathVariable String name) throws IOException {
		request.getUserPrincipal().getName();
		
		gameCore.logoutPlayer(name);
		
		return ControllerUtil.createEmptyResponse("", HttpStatus.OK);
	}	
	
	public class PlayerBuilder {
		private String name;
		private String password;
		
		public void setName(String name) {
			this.name = name;
		}
		
		public void setPassword(String password) {
			this.password = password;
		}
		
		public Player createPlayer() {
			return new PlayerImpl(name, password);
		}
	}
	
	private class PaginatedList<T>  extends ArrayList<T> {
		private static final long serialVersionUID = -2193285582777892978L;
		
		private int from;
		private int to;
		
		public PaginatedList(List<T> list, String from, String to, int defaultCount) {
			if (from == null && to == null) {
				this.from = 0;
				this.to = defaultCount - 1;
			}
			else if (to == null) {
				this.from = Integer.parseInt(from);
				this.to = this.from + defaultCount - 1;
			}
			else if (from == null) {
				this.to = Integer.parseInt(to);
				this.from = this.to - defaultCount + 1;
			}
			else {
				this.from = Integer.parseInt(from);
				this.to = Integer.parseInt(to);
			}
			if (this.from < 0)
				this.from = 0;
			if (this.to < 0)
				this.to = this.from + defaultCount - 1;
			for (int i = this.from; i < list.size() && i <= this.to; i++) {
				this.add(list.get(i));
			}
		}
	}


	private static void prepare(HttpServletRequest request) {
		String serverAddress;
		if (request.getServerPort() == 80)
			serverAddress = request.getScheme() + "://"
					+ request.getServerName();
		else
			serverAddress = request.getScheme() + "://"
					+ request.getServerName() + ":" + request.getServerPort();
		request.setAttribute("serverAddress", serverAddress);
	}
}
